package cn.fraudmetrix.octopus.modules.jacoco.service;

import cn.fraudmetrix.octopus.modules.jacoco.constant.JacocoConstant;
import com.github.javaparser.JavaParser;
import com.github.javaparser.ast.CompilationUnit;
import com.github.javaparser.ast.body.MethodDeclaration;
import com.github.javaparser.ast.visitor.VoidVisitorAdapter;
import org.dstadler.Account;
import org.eclipse.jgit.api.CloneCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.transport.UsernamePasswordCredentialsProvider;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.*;

/**
 * @author libao.zheng
 * @email libao.zheng@tongdun.cn
 * @date 2018/12/5 11:26
 */
public class ShowBranchDiff {
    private final Logger LOGGER = LoggerFactory.getLogger(getClass());
    private List<String> newClass = new ArrayList<>();
    private List<String> modifiedClass = new ArrayList<>();
    private List<String> modifiedJava = new ArrayList<>();
    private Set<String> modifiedMethod = new HashSet<>();
    private Map<String, Integer> newMethodInfo = new HashMap<>();
    private Map<String, Integer> oldMethodInfo = new HashMap<>();

    public List<String> getNewClass() {
        return newClass;
    }

    public void setNewClass(List<String> newClass) {
        this.newClass = newClass;
    }

    public List<String> getModifiedClass() {
        return modifiedClass;
    }

    public void setModifiedClass(List<String> modifiedClass) {
        this.modifiedClass = modifiedClass;
    }

    public List<String> getModifiedJava() {
        return modifiedJava;
    }

    public void setModifiedJava(List<String> modifiedJava) {
        this.modifiedJava = modifiedJava;
    }

    public Set<String> getModifiedMethod() {
        return modifiedMethod;
    }

    public void setModifiedMethod(Set<String> modifiedMethod) {
        this.modifiedMethod = modifiedMethod;
    }

    public Map<String, Integer> getNewMethodInfo() {
        return newMethodInfo;
    }

    public void setNewMethodInfo(Map<String, Integer> newMethodInfo) {
        this.newMethodInfo = newMethodInfo;
    }

    public Map<String, Integer> getOldMethodInfo() {
        return oldMethodInfo;
    }

    public void setOldMethodInfo(Map<String, Integer> oldMethodInfo) {
        this.oldMethodInfo = oldMethodInfo;
    }

    /**
     * 下载分支代码到本地
     *
     * @param gitAddr
     * @param appName
     * @param branchName
     * @return true 有代码更新 false无代码更新
     * @throws IOException
     * @throws GitAPIException
     */
    public boolean cloneBranch(String gitAddr, String appName, String branchName) throws IOException, GitAPIException {
        String path = JacocoConstant.LOCAL_RESOURCE + appName + "/" + branchName;
        File localPath = new File(path);
        if (localPath.exists() && localPath.isDirectory()) {
            LOGGER.warn("path:{} is exists,git pull begin", path);
            String s = Shell.execCmd(JacocoConstant.GIT_PULL_CMD, new File(path));
            return !s.contains(JacocoConstant.GIT_PULL_MSG);
        }
        CloneCommand cloneCommand = Git.cloneRepository();
        cloneCommand.setDirectory(localPath);
        cloneCommand.setBranch(branchName);
        cloneCommand.setURI(gitAddr);
        cloneCommand.setCredentialsProvider(new UsernamePasswordCredentialsProvider(JacocoConstant.USER_NAME, JacocoConstant.PASS_WORD));
        cloneCommand.call();
        return true;
    }


    /**
     * 获取新增或修改的类名
     *
     * @param appName
     * @param git
     * @param newTreeParser
     * @param oldTreeParser
     */
    public void getDiffClass(String appName, Git git, AbstractTreeIterator newTreeParser, AbstractTreeIterator oldTreeParser) throws GitAPIException {
        List<DiffEntry> diff = git.diff().setOldTree(oldTreeParser).setNewTree(newTreeParser).call();
        for (DiffEntry entry : diff) {
            String newPath = entry.getNewPath();
            String name = entry.getChangeType().name();
            String classPath = JacocoConstant.LOCAL_RESOURCE + appName + "/" + newPath;
            if (newPath.endsWith(JacocoConstant.JAVA_SUFFIX)) {
                if (JacocoConstant.TAG_ADD.equals(name)) {
                    newClass.add(newPath.substring(newPath.indexOf(JacocoConstant.LOCAL_SOURCE) + 14).replace(JacocoConstant.JAVA_SUFFIX, JacocoConstant.CLASS_SUFFIX));
                }
                if (JacocoConstant.TAG_MODIFY.equals(name)) {
                    modifiedJava.add(classPath);
                    modifiedClass.add(newPath.substring(newPath.indexOf(JacocoConstant.LOCAL_SOURCE) + 14).replace(JacocoConstant.JAVA_SUFFIX, JacocoConstant.CLASS_SUFFIX));
                }
            }
        }
    }

    /**
     * 获取修改的方法
     *
     * @param appName
     * @param branchName
     * @param modifiedJava
     */
    public void getDiffMethod(String appName, String branchName, String modifiedJava) throws FileNotFoundException {
        String replace = modifiedJava.replace(JacocoConstant.LOCAL_RESOURCE + appName, JacocoConstant.LOCAL_RESOURCE + appName + "/" + branchName);
        FileInputStream in = new FileInputStream(replace);
        CompilationUnit cu = JavaParser.parse(in);
        cu.accept(new MethodVisitor(branchName, modifiedJava), null);
    }

    public AbstractTreeIterator prepareTreeParser(Repository repository, String ref) throws Exception {
        Ref head = repository.exactRef(ref);
        RevWalk walk = new RevWalk(repository);
        RevCommit commit = walk.parseCommit(head.getObjectId());
        RevTree tree = walk.parseTree(commit.getTree().getId());
        CanonicalTreeParser treeParser = new CanonicalTreeParser();
        ObjectReader reader = repository.newObjectReader();
        treeParser.reset(reader, tree.getId());
        walk.dispose();
        return treeParser;
    }


    public class MethodVisitor extends VoidVisitorAdapter<Void> {
        private String branchName;
        private String modifiedClass;

        public MethodVisitor(String branchName, String modifiedClass) {
            this.branchName = branchName;
            this.modifiedClass = modifiedClass;
        }

        @Override
        public void visit(MethodDeclaration n, Void arg) {
            String methodName = n.getNameAsString();
            if (JacocoConstant.MASTER_BRANCH.equals(branchName)) {
                oldMethodInfo.put(modifiedClass.replace(JacocoConstant.JAVA_SUFFIX, "") + "/" + methodName, n.hashCode());
            } else {
                newMethodInfo.put(modifiedClass.replace(JacocoConstant.JAVA_SUFFIX, "") + "/" + methodName, n.hashCode());
            }
        }
    }
}


